﻿using SharpDepend.Architecture.Core;
using SharpDepend.Classes.Threading;
using System;
using System.Collections.Generic;
using System.Linq;

namespace SharpDepend.Architecture.Windows
{
    public interface IBaseWindowContent<TApplication> : IBasePage<TApplication>
    {
        /// <summary>
        /// Get the window title.
        /// </summary>
        string Title { get; }

        /// <summary>
        /// This initialize calls once when the window is registrated.
        /// </summary>
        /// <param name="application"></param>
        /// <param name="window"></param>
        void Initialize(TApplication application, IWindowBase<TApplication> window);

        /// <summary>
        /// Calls when window is shown.
        /// </summary>
        /// <param name="parameter">Optional parameter.</param>
        void OnShown(object parameter);

        /// <summary>
        /// Calls when windows has closed.
        /// </summary>
        void OnClose();
    }

    /// <summary>
    /// A window with only one instance per window.
    /// </summary>
    public interface IWindowContent<TController, TContext>
        : IBaseWindowContent<TContext>, IViewer<TController, TContext> where TController : class, IController<TContext>
    {
    }

    public interface IWindowBase<TApplication>
    {
        /// <summary>
        /// Get the window content instance.
        /// </summary>
        IBaseWindowContent<TApplication> Window { get; }

        /// <summary>
        /// Return true if the window is open.
        /// </summary>
        bool IsOpen { get; }

        /// <summary>
        /// Calls when window is closing.
        /// </summary>
        event Action OnClosing;

        /// <summary>
        /// Show the window.
        /// </summary>
        /// <param name="parameter">Optional parameter.</param>
        void Show(object parameter);

        /// <summary>
        /// Close window.
        /// </summary>
        void Close();
    }

    public class WindowManager<TApplication, TApplicationCore>
        where TApplication : IBaseApplication
        where TApplicationCore : BaseApplicationCore<TApplication, TApplicationCore>
    {
        private TApplication mApplication;
        private List<IWindowBase<TApplication>> mWindows;
        private ThreadChecker mThreadChecker = new ThreadChecker();

        public WindowManager(TApplication application)
        {
            mApplication = application;
            mWindows = new List<IWindowBase<TApplication>>();
        }

        /// <summary>
        /// Registrate window.
        /// </summary>
        /// <param name="window">Window.</param>
        public void RegisterWindow(IWindowBase<TApplication> window)
        {
            mThreadChecker.Check();

            if (window == null)
            {
                throw new ArgumentNullException(nameof(window));
            }

            if (mWindows.Contains(window))
            {
                throw new Exception("Windows already registrated.");
            }

            mWindows.Add(window);
            window.Window.Controller.Initialize(mApplication);
        }

        /// <summary>
        /// Close dialog if it is open.
        /// </summary>
        /// <typeparam name="T">Window</typeparam>
        public void CloseWindow<T>() where T : IBaseWindowContent<TApplication>
        {
            mThreadChecker.Check();

            IWindowBase<TApplication> baseWindow;
            GetWindow<T>(out baseWindow);
            if (baseWindow.IsOpen)
            {
                baseWindow.Close();
            }
        }

        /// <summary>
        /// Show window by interface type with no parameter.
        /// </summary>
        /// <typeparam name="T">Window interface type.</typeparam>
        public T ShowWindow<T>() where T : IBaseWindowContent<TApplication>
        {
            return ShowWindow<T>(null);
        }

        /// <summary>
        /// Show window by interface type.
        /// </summary>
        /// <typeparam name="T">Window interface type.</typeparam>
        /// <param name="parameter">Optional parameter.</param>
        public T ShowWindow<T>(object parameter) where T : IBaseWindowContent<TApplication>
        {
            mThreadChecker.Check();

            IWindowBase<TApplication> window = _GetWindow<T>();
            window.Show(parameter);
            return (T)window.Window;
        }

        /// <summary>
        /// Get window by interface type.
        /// <para>User this to lazy show the window.</para>
        /// </summary>
        /// <typeparam name="T">Window interface type.</typeparam>
        /// <param name="actualWindow">Return the actual window instnace.</param>
        /// <returns>Window.</returns>
        public T GetWindow<T>(out IWindowBase<TApplication> actualWindow) where T : IBaseWindowContent<TApplication>
        {
            mThreadChecker.Check();

            // Returnera ut native window för att öra show() så att den nyss.
            actualWindow = _GetWindow<T>();
            return (T)actualWindow.Window;
        }

        private IWindowBase<TApplication> _GetWindow<T>() where T : IBaseWindowContent<TApplication>
        {
            mThreadChecker.Check();

            foreach (var window in mWindows)
            {
                Type type = window.Window.GetType();
                Type[] interfaces = type.GetInterfaces();

                // Check the window's all interfaces and check if it matches.
                if (interfaces.Contains(typeof(T)))
                {
                    return window;
                }
            }

            // If you get this exception, you may forgot to call something like this:
            // mApplication.Core.Windows.RegisterWindow<...>(...);
            throw new Exception("Window of type '" + typeof(T).Name + "' is not registrated.");
        }

        /// <summary>
        /// Check if a window is open.
        /// </summary>
        /// <typeparam name="T">Window interface type.</typeparam>
        /// <returns>True if the window is open.</returns>
        public bool IsWindowsOpen<T>() where T : IBaseWindowContent<TApplication>
        {
            mThreadChecker.Check();

            IWindowBase<TApplication> window = _GetWindow<T>();

            return window.IsOpen;
        }
    }
}
